import 'package:winkai/db/db_ext.dart';
import 'package:winkai/entity/msg_constant.dart';
import 'package:drift/drift.dart';

import '../database.dart';
import '../tables/msg_tb.dart';
import '../tables/ses_tb.dart';
import '../tables/user_tb.dart';

part 'msg_dao.g.dart';

class MsgInfo {
  MsgTbData msg;

  final UserTbData? user;

  final SesTbData session;

  MsgInfo({
    required this.msg,
    required this.session,
    this.user,
  });
}

@DriftAccessor(tables: [
  MsgTb,
  SesTb,
  UserTb,
])
class MsgDao extends DatabaseAccessor<RootDatabase> with _$MsgDaoMixin {
  MsgDao(super.attachedDatabase);

  Selectable<MsgInfo> _selectJoins(
      SimpleSelectStatement<$MsgTbTable, MsgTbData> s) {
    return s.join([
      leftOuterJoin(
          userTb, userTb.accountExpr & userTb.id.equalsExp(msgTb.userId)),
      leftOuterJoin(sesTb, sesTb.id.equalsExp(msgTb.sessionId)),
    ]).map((row) => MsgInfo(
          msg: row.readTable(msgTb),
          user: row.readTableOrNull(userTb),
          session: row.readTable(sesTb),
        ));
  }

  Future<int> insertOrUpdate(MsgTbCompanion entity) {
    return into(msgTb).insertOnConflictUpdate(entity);
  }

  /// 批量插入或更新消息
  Future<void> batchInsertOrUpdate(Iterable<MsgTbCompanion> items) async {
    await batch((batch) {
      batch.insertAllOnConflictUpdate(msgTb, items);
    });
  }

  /// 通过会话id查询
  Future<List<MsgInfo>> selectBySessionId(String sessionId,
      {bool isAsc = true, int? limit, int? offset}) {
    var s = select(msgTb)
      ..where((tbl) => tbl.accountExpr & tbl.sessionId.equals(sessionId));
    if (limit != null) {
      s.limit(limit, offset: offset);
    }
    s.orderBy([
      (e) => isAsc
          ? OrderingTerm.asc(e.createTime)
          : OrderingTerm.desc(e.createTime)
    ]);
    return _selectJoins(s).get();
  }

  Future<List<MsgInfo>> selectBySessionIdAndIsTop(
      String sessionId, bool isTop) {
    return _selectJoins((select(msgTb)
          ..where((tbl) =>
              tbl.accountExpr &
              tbl.sessionId.equals(sessionId) &
              tbl.isTop.equals(isTop))
          ..orderBy([(e) => OrderingTerm.asc(e.updateTime)])))
        .get();
  }

  Future<MsgInfo?> selectByIdSingleOrNull(int id) async {
    final res = await _selectJoins((select(msgTb)
          ..where((tbl) => tbl.accountExpr & tbl.id.equals(id))))
        .get();
    return res.firstOrNull;
  }

  Future<List<MsgInfo>> selectById(List<int> ids) {
    return _selectJoins(
            (select(msgTb)..where((tbl) => tbl.accountExpr & tbl.id.isIn(ids))))
        .get();
  }

  Future<List<MsgInfo>> selectByIds(Iterable<int> ids) {
    return _selectJoins(
            (select(msgTb)..where((tbl) => tbl.accountExpr & tbl.id.isIn(ids))))
        .get();
  }

  Future<int> updateByStatus(int id, MsgStatus status) {
    return (update(msgTb)..where((tbl) => tbl.accountExpr & tbl.id.equals(id)))
        .write(MsgTbCompanion(
      status: Value(status),
      updateTime: Value(DateTime.now()),
    ));
  }

  Future<int> deleteBySessionId(String sessionId) {
    return (delete(msgTb)
          ..where((tbl) => tbl.accountExpr & tbl.sessionId.equals(sessionId)))
        .go();
  }

  Future<int> deleteById(int id) {
    return (delete(msgTb)..where((tbl) => tbl.accountExpr & tbl.id.equals(id)))
        .go();
  }

  Future<int> updateContent(int id, String content) {
    return (update(msgTb)..where((tbl) => tbl.accountExpr & tbl.id.equals(id)))
        .write(MsgTbCompanion(
      content: Value(content),
      updateTime: Value(DateTime.now()),
    ));
  }

  Future<int> deleteByCurrentAccount() {
    return (delete(msgTb)..where((tbl) => tbl.accountExpr)).go();
  }

  Future<int> updateIsTopById(List<int> ids, bool isTop) {
    return (update(msgTb)..where((tbl) => tbl.accountExpr & tbl.id.isIn(ids)))
        .write(MsgTbCompanion(
      isTop: Value(isTop),
      updateTime: Value(DateTime.now()),
    ));
  }

  Future<int> updateIsTopBySessionId(String sessionId, bool isTop) {
    return (update(msgTb)
          ..where((tbl) => tbl.accountExpr & tbl.sessionId.equals(sessionId)))
        .write(MsgTbCompanion(
      isTop: Value(isTop),
      updateTime: Value(DateTime.now()),
    ));
  }

  Future<int> resetIsRead(String sessionId) {
    return (update(msgTb)
          ..where((tbl) => tbl.accountExpr & tbl.sessionId.equals(sessionId)))
        .write(
      const MsgTbCompanion(isRead: Value(true)),
    );
  }
}
